import type { TruncateOptions } from '../types'

export interface CalculateFieldValueOptions {
  defaultValue?: any
  precision?: number
  returnType?: 'value' | 'item'
}

/**
 * 安全字符串处理接口
 */
interface SafeStringOptions {
  trim?: boolean
  removeExtraSpaces?: boolean
  toLowerCase?: boolean
  defaultValue?: string
}

/**
 * 字符串处理工具类
 */
export class StringUtils {
  /**
   * 高级字符串截取函数
   * @param value - 原始字符串
   * @param limit - 最大长度
   * @param options - 配置项
   *
   * @example
   * truncate('Hello World', 5)                         // "Hello..."
   * truncate('Hello World', 5, { suffix: '→' })        // "Hello→"
   * truncate('Hello World', 6, { keepFullWords: true }) // "Hello..."（而不是"Hell..."）
   */
  static truncate(
    value: string,
    limit: number,
    options: TruncateOptions = {},
  ): string {
    const { suffix = '...', keepFullWords = false } = options

    if (!value)
      return ''

    if (value.length <= limit)
      return value

    if (keepFullWords) {
      const lastSpaceIndex = value.slice(0, limit + 1).lastIndexOf(' ')
      if (lastSpaceIndex > 0) {
        return `${value.slice(0, lastSpaceIndex)}${suffix}`
      }
    }

    return `${value.slice(0, limit)}${suffix}`
  }

  /**
   * 首字母大写
   * @param str 字符串
   * @returns 首字母大写的字符串
   * @example
   * capitalizeFirstLetter('hello') // 'Hello'
   */
  static capitalizeFirstLetter(str: string): string {
    return str.charAt(0).toUpperCase() + str.slice(1)
  }

  /**
   * 将驼峰命名转换为连字符命名
   * @param str 要转换的字符串
   * @returns 转换后的连字符命名
   * @example
   * camelToKebab('helloWorld') // 'hello-world'
   */
  static camelToKebab(str: string): string {
    return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
  }

  /**
   * 将连字符命名转换为驼峰命名
   * @param str 要转换的字符串
   * @returns 转换后的驼峰命名
   * @example
   * kebabToCamel('hello-world') // 'helloWorld'
   */
  static kebabToCamel(str: string): string {
    return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())
  }

  /**
   * 移除字符串中的空白行
   * @param content 原始字符串
   * @param options
   * @param options.removeLeadingTrailing 是否移除首尾空白行
   * @param options.removeMultipleEmptyLines 是否移除连续多个空白行
   * @returns 处理后的字符串
   */
  static removeEmptyLines(
    content: string,
    options?: {
      removeLeadingTrailing?: boolean
      removeMultipleEmptyLines?: boolean
    },
  ): string {
    let result = content
    const { removeLeadingTrailing = true, removeMultipleEmptyLines = true } = options || {}

    // 移除连续多个空白行
    if (removeMultipleEmptyLines) {
      result = result.replace(/\n\s*\n/g, '\n')
    }

    // 移除行首和行尾的空白字符
    result = result.replace(/^\s+|\s+$/gm, '')

    // 移除首尾空白行
    if (removeLeadingTrailing) {
      result = result.replace(/^\n+|\n+$/g, '')
    }

    return result
  }

  /**
   * 转义 HTML 特殊字符
   * @param unsafe 包含特殊字符的字符串
   * @returns 转义后的字符串
   * @example
   * escapeHtml('<>&"\'') // '&lt;&gt;&amp;&quot;&#039;'
   */
  static escapeHtml(unsafe: string): string {
    return unsafe
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#039;')
  }

  /**
   * 转义正则特殊字符
   */
  static escapeRegExp(str: string): string {
    return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
  }

  /**
   * 从文本中提取所有邮箱地址
   * @private
   * @param {string} text - 需要提取的文本内容
   * @returns {string[]} 提取到的邮箱地址数组
   * @example
   * const emails = extractEmails('联系邮箱: example@domain.com')
   * // returns: ['example@domain.com']
   */
  static extractEmails(text: string): string[] {
    if (!text || typeof text !== 'string') {
      return []
    }

    // 更精确的邮箱正则表达式
    const emailRegex = /[\w.%+-]+@[a-z0-9.-]+\.[a-z]{2,}(?:\.[a-z]{2,})?/gi

    const emails = text.match(emailRegex) || []

    // 去重并过滤空值
    return [...new Set(emails)].filter(email =>
      email && email.length > 0 && email.includes('@'),
    )
  }

  /**
   * 比较两个版本号
   * @param v1 - 版本号1
   * @param v2 - 版本号2
   * @returns
   *   - 0: 版本相同
   *   - 1: v1 > v2
   *   - -1: v1 < v2
   */
  static compareVersions(v1: string, v2: string): number {
    const parts1 = v1.split('.').map(Number)
    const parts2 = v2.split('.').map(Number)

    for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
      const num1 = parts1[i] || 0
      const num2 = parts2[i] || 0

      if (num1 > num2)
        return 1
      if (num1 < num2)
        return -1
    }

    return 0
  }

  /**
   * 计算文本相似度（使用编辑距离算法）
   * @param str1 - 文本1
   * @param str2 - 文本2
   * @returns {number} 相似度 (0-1)
   */
  static calculateSimilarity(str1: string, str2: string): number {
    if (str1 === str2)
      return 1.0

    const longer = str1.length > str2.length ? str1 : str2
    const shorter = str1.length > str2.length ? str2 : str1

    // 如果长度差异太大，直接返回低相似度
    if (longer.length - shorter.length > longer.length * 0.3) {
      return 0
    }

    // 使用编辑距离计算相似度
    const distance = this.calculateLevenshteinDistance(str1, str2)
    const maxLength = Math.max(str1.length, str2.length)

    if (maxLength === 0)
      return 1.0

    return 1 - distance / maxLength
  }

  /**
   * 计算 Levenshtein 编辑距离
   * @param str1 - 文本1
   * @param str2 - 文本2
   * @returns {number} 编辑距离
   */
  static calculateLevenshteinDistance(str1: string, str2: string): number {
    const matrix: number[][] = []

    // 初始化矩阵
    for (let i = 0; i <= str2.length; i++) {
      matrix[i] = [i]
    }
    for (let j = 0; j <= str1.length; j++) {
      matrix[0][j] = j
    }

    // 填充矩阵
    for (let i = 1; i <= str2.length; i++) {
      for (let j = 1; j <= str1.length; j++) {
        if (str2.charAt(i - 1) === str1.charAt(j - 1)) {
          matrix[i][j] = matrix[i - 1][j - 1]
        }
        else {
          matrix[i][j] = Math.min(
            matrix[i - 1][j - 1] + 1, // 替换
            matrix[i][j - 1] + 1, // 插入
            matrix[i - 1][j] + 1, // 删除
          )
        }
      }
    }

    return matrix[str2.length][str1.length]
  }

  /**
   * 生成文本哈希值
   */
  static generateTextHash(text: string): string {
    // 使用简单哈希算法，性能更好
    let hash = 0

    for (let i = 0; i < text.length; i++) {
      const char = text.charCodeAt(i)
      hash = ((hash << 5) - hash) + char
      hash = hash & hash // Convert to 32bit integer
    }
    return Math.abs(hash).toString(36)
  }

  /**
   * 解码 HTML 实体字符
   * @param text - 包含HTML实体的文本
   * @returns {string} 解码后的文本
   */
  static decodeHtmlEntities(text: string): string {
    const textArea = document.createElement('textarea')
    textArea.innerHTML = text
    return textArea.value
  }

  /**
   * 从数组中提取最大、最小、平均或求和的字段值
   */
  static getExtremeFieldValue<T>(
    items: T[],
    field: keyof T,
    type: 'min' | 'max' | 'avg' | 'sum',
    dataType: 'date' | 'number' = 'number',
    options: CalculateFieldValueOptions = {},
  ): T[keyof T] | T | '' {
    const otherOptions = { defaultValue: '', returnType: 'value', precision: 2, ...options } as CalculateFieldValueOptions

    if (items.length === 0) {
      return otherOptions.defaultValue
    }

    // 根据类型选择计算方式
    switch (type) {
      case 'avg':
        return this.calculateAverageFieldValue(items, field, dataType, otherOptions)
      case 'sum':
        return this.calculateSumFieldValue(items, field, dataType, otherOptions)
      case 'min':
      case 'max':
      default:
        return this.calculateMinMaxFieldValue(items, field, type, dataType, otherOptions)
    }
  }

  /**
   * 从数组中提取最大或最小的字段值
   */
  static calculateMinMaxFieldValue<T>(
    items: T[],
    field: keyof T,
    type: 'min' | 'max',
    dataType: 'date' | 'number',
    options: CalculateFieldValueOptions = {},
  ): T[keyof T] | T | '' {
    const { returnType = 'value', defaultValue = '' } = options

    if (items.length === 0) {
      return defaultValue
    }

    const result = items.reduce((prev, current) => {
      let prevValue = prev[field] as number
      let currentValue = current[field] as number

      // 处理日期字符串或数字
      if (dataType === 'date') {
        prevValue = new Date(prevValue).getTime()
        currentValue = new Date(currentValue).getTime()
      }

      if (type === 'min') {
        return prevValue < currentValue ? prev : current
      }
      else {
        return prevValue > currentValue ? prev : current
      }
    })

    return returnType === 'item' ? result : result[field] || defaultValue
  }

  /**
   * 计算字段的平均值
   */
  private static calculateAverageFieldValue<T>(
    items: T[],
    field: keyof T,
    dataType: 'date' | 'number',
    options: CalculateFieldValueOptions,
  ): T[keyof T] | '' {
    const { precision = 2, defaultValue = '' } = options

    const validItems = items.filter((item) => {
      const value = this.getFieldValue(item, field, dataType, false)
      return value !== null && !Number.isNaN(value) && Number.isFinite(value)
    })

    if (validItems.length === 0) {
      return defaultValue
    }

    const sum = validItems.reduce((total, item) => {
      const value = this.getFieldValue(item, field, dataType, false)!
      return total + value
    }, 0)

    const average = sum / validItems.length

    // 根据数据类型返回结果
    if (dataType === 'date') {
      // 对于日期，返回平均时间戳对应的日期字符串
      const avgDate = new Date(average)
      return avgDate.toISOString() as T[keyof T]
    }
    else {
      // 对于数字，返回格式化后的数值
      const formattedValue = Number(average.toFixed(precision))
      return formattedValue as T[keyof T]
    }
  }

  /**
   * 计算字段的总和
   */
  private static calculateSumFieldValue<T>(
    items: T[],
    field: keyof T,
    dataType: 'date' | 'number',
    options: CalculateFieldValueOptions,
  ): T[keyof T] | '' {
    const { precision = 2, defaultValue = '' } = options

    const validItems = items.filter((item) => {
      const value = this.getFieldValue(item, field, dataType, false)
      return value !== null && !Number.isNaN(value) && Number.isFinite(value)
    })

    if (validItems.length === 0) {
      return defaultValue
    }

    const sum = validItems.reduce((total, item) => {
      const value = this.getFieldValue(item, field, dataType, false)!
      return total + value
    }, 0)

    // 根据数据类型返回结果
    if (dataType === 'date') {
    // 对于日期，返回总时间戳对应的日期字符串
      const sumDate = new Date(sum)
      return sumDate.toISOString() as T[keyof T]
    }
    else {
    // 对于数字，返回格式化后的数值
      const formattedValue = Number(sum.toFixed(precision))
      return formattedValue as T[keyof T]
    }
  }

  /**
   * 提取字段值并转换为数字
   */
  private static getFieldValue<T>(
    item: T,
    field: keyof T,
    dataType: 'date' | 'number',
    filterInvalid: boolean,
  ): number | null {
    const rawValue = item[field]

    if (rawValue === null || rawValue === undefined || rawValue === '') {
      return filterInvalid ? null : 0
    }

    try {
      if (dataType === 'date') {
      // 处理日期类型
        const date = new Date(rawValue as any)
        return Number.isNaN(date.getTime()) ? (filterInvalid ? null : 0) : date.getTime()
      }
      else {
      // 处理数字类型
        const num = typeof rawValue === 'number'
          ? rawValue
          : Number(rawValue)
        return Number.isNaN(num) ? (filterInvalid ? null : 0) : num
      }
    }
    catch {
      return filterInvalid ? null : 0
    }
  }

  /**
   * 清理文本中的多余内容
   */
  static cleanText(text: string, patterns: RegExp[]): string {
    if (!text)
      return ''

    let cleanedText = text

    // 应用所有过滤规则
    patterns.forEach((pattern) => {
      cleanedText = cleanedText.replace(pattern, ' ')
    })

    // 最终清理：去除多余空格，trim
    cleanedText = cleanedText.replace(/\s+/g, ' ').trim()

    return cleanedText
  }

  /**
   * 生成唯一 ID（使用现代 Web Crypto API）
   * 1）保留时间戳的优点
   * 可读性：时间戳让 ID 更容易理解（知道何时创建）
   * 排序能力：可以按创建时间排序
   * 调试友好：便于追踪问题发生的时间点
   * 兼容性：与现有系统可能依赖时间信息
   * 2）去掉时间戳的优点
   * 更短：ID长度减少约 13-15 个字符
   * 更简洁：去掉冗余信息
   * 隐私保护：不暴露创建时间信息
   */
  static generateUniqueId(options?: { prefix?: string, length?: number, withTime?: boolean, separator?: 'keep' | 'dash' | 'underscore' }): string {
    const {
      prefix = '',
      length = 8,
      withTime = true,
      separator = 'keep',
    } = options || {}

    // 参数验证
    if (length < 4 || length > 32) {
      throw new Error('ID长度必须在4-32之间')
    }

    if (prefix && !/^[\w-]+$/.test(prefix)) {
      throw new Error('前缀只能包含字母、数字、下划线和连字符')
    }

    // 生成 UUID 并截取指定长度
    const uuid = crypto.randomUUID()
    const shortId = uuid.replace(/-/g, '').slice(0, length)

    // 构建基础 ID 部分
    const baseParts: string[] = []

    if (prefix) {
      baseParts.push(prefix)
    }

    if (withTime) {
      baseParts.push(Date.now().toString())
    }

    baseParts.push(shortId)

    // 根据分隔符选项处理
    let result: string

    switch (separator) {
      case 'dash':
        // 统一为连字符，将下划线转换为连字符
        result = baseParts.map(part => part.replace(/_/g, '-')).join('-')
        break

      case 'underscore':
        // 统一为下划线，将连字符转换为下划线
        result = baseParts.map(part => part.replace(/-/g, '_')).join('_')
        break

      case 'keep':
      default:
        // 保持原样，使用连字符连接
        result = baseParts.join('-')
        break
    }

    return result
  }

  /**
   * 检查元素文本是否包含指定的文本
   */
  static includesText($element: JQuery<HTMLElement>, ...texts: string[]): boolean {
    const elementText = $element.text().trim().toLowerCase()

    const searchTexts = texts.flat().map(text => text.toLowerCase())
    return searchTexts.some(text => elementText.includes(text))
  }

  /**
   * 安全处理字符串
   */
  static safeString(str: string | undefined | null, options: SafeStringOptions = {}): string {
    const {
      trim = true,
      removeExtraSpaces = false,
      toLowerCase = false,
      defaultValue = '',
    } = options

    // 处理空值
    if (str == null || str === '')
      return defaultValue

    let result = String(str)

    // 按顺序应用处理规则
    if (trim)
      result = result.trim()

    if (removeExtraSpaces)
      result = result.replace(/\s+/g, ' ')

    if (toLowerCase)
      result = result.toLowerCase()

    return result
  }

  /**
   * 连接字符串列表
   */
  static contactList(items: string[] | StringMap, separator: string = '-'): string {
    const itemList = Array.isArray(items)
      ? items
      : Object.values(items)

    return itemList.map(item => this.safeString(item)).filter(item => item !== '').join(separator)
  }

  /**
   * 检查内容是否匹配指定模式
   * @param inputContent 要检查的输入内容
   * @param pattern 匹配模式配置
   * @returns 是否匹配成功
   */
  static isMatch(inputContent: string, pattern: MatchPattern): boolean {
    // 1. 检查字符串匹配
    if (typeof pattern === 'string') {
      return pattern.length > 0 && inputContent.includes(pattern)
    }
    // 2. 检查正则表达式匹配
    else if (pattern instanceof RegExp) {
      return pattern.test(inputContent)
    }
    // 3. 检查自定义函数匹配
    else if (typeof pattern === 'function') {
      return pattern(inputContent)
    }
    // 4. 检查字符串数组匹配（任一匹配即可）
    else if (Array.isArray(pattern)) {
      return pattern.some((item: string) => inputContent.includes(item))
    }

    return false
  }

  /**
   * 向字符串数组添加内容并去重
   * @param originalArray 原始数组
   * @param newItems 要添加的新内容（支持字符串或字符串数组）
   * @returns 去重后的新数组
   */
  static addToArrayWithDeduplication(
    originalArray: string[],
    newItems: string | string[],
  ): string[] {
    const itemsToAdd = Array.isArray(newItems) ? newItems : [newItems]
    const combinedArray = [...originalArray, ...itemsToAdd]
    return [...new Set(combinedArray)]
  }
}
